﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace GameDriver
{
    /// <summary>
    /// A class representing an individual scene from a game, which is further composed of layers.
    /// This class shouldn't normally be sub-classed, but instantiated with custom layers.
    /// </summary>
    public class Scene
    {
        #region PRIVATE VARIABLES
        private List<Layer> layers;
        private bool breakUpdate = false;
        #endregion

        #region PUBLIC VARIABLES AND PROPERTIES
        /// <summary>The parent SceneManager for this scene.</summary>
        public SceneManager Manager;
        /// <summary>The ContentHandler belonging to this scene.</summary>
        public ContentHandler Content;
        #endregion

        /// <summary>
        /// Create a new scene.
        /// </summary>
        /// <param name="m">
        /// The scene manager for this scene - this must be declared
        /// up front to allow for the initialization of the ContentHandler.
        /// </param>
        public Scene(SceneManager m)
        {
            Manager = m;
            Content = new ContentHandler(m.Game.Content.ServiceProvider);
            layers = new List<Layer>();
        }

        #region Public Methods
        /// <summary>
        /// Add a layer to the scene.  This ends the current update cycle.
        /// </summary>
        /// <param name="l">The layer to add.</param>
        public void AddLayer(Layer l)
        {
            l.Scene = this;
            layers.Add(l);
            breakUpdate = true;
        }

        /// <summary>
        /// Retrieve a layer based on numerical index.
        /// </summary>
        /// <param name="i">The index to check.</param>
        /// <returns>The layer at the given index if it exists, or null otherwise.</returns>
        public Layer GetLayer(int i)
        {
            if (i < 0 || i >= layers.Count)
                return null;
            return layers.ElementAt(i);
        }
        /// <summary>
        /// Retrieve a layer based on type.
        /// </summary>
        /// <param name="t">The type of layer to look for.</param>
        /// <returns>The first layer of the given type if any, or null otherwise.</returns>
        public Layer GetLayer(Type t)
        {
            for (int i = layers.Count - 1; i >= 0; i--)
                if (layers[i].GetType() == t)
                    return layers.ElementAt(i);
            return null;
        }

        /// <summary>
        /// Remove the given layer.
        /// </summary>
        /// <param name="l">The layer to remove.</param>
        public void RemoveLayer(Layer l)
        {
            l.Scene = null;
            layers.Remove(l);
            breakUpdate = true;
        }
        /// <summary>
        /// Remove the layer at the given numerical index.
        /// </summary>
        /// <param name="i">The index of the layer to remove.</param>
        public void RemoveLayer(int i)
        {
            layers[i].Scene = null;
            layers.RemoveAt(i);
            breakUpdate = true;
        }
        /// <summary>
        /// Remove the first layer of the given type.
        /// </summary>
        /// <param name="t">The type of layer to remove.</param>
        public void RemoveLayer(Type t)
        {
            for (int i = layers.Count - 1; i >= 0; i--)
                if (layers[i].GetType() == t)
                {
                    layers[i].Scene = null;
                    layers.RemoveAt(i);
                    breakUpdate = true;
                }
        }

        /// <summary>
        /// Updates all unpaused layers in the scene.
        /// </summary>
        /// <param name="gt">The current running/passed time for the game.</param>
        public void Update(GameTime gt)
        {
            foreach (Layer l in layers)
                if (!l.Paused)
                {
                    l.Update(gt);
                    if (breakUpdate)
                        break;
                }
            breakUpdate = false;
        }
        /// <summary>
        /// Draws all visible layers in the scene.
        /// </summary>
        /// <param name="sb">The SpriteBatch to use for rendering.</param>
        public void Draw(SpriteBatch sb)
        {
            foreach (Layer l in layers)
                if (l.Visible)
                    l.Draw(sb);
        }

        /// <summary>
        /// Pause all the layers in the scene and unload the content.
        /// </summary>
        public void Pause()
        {
            Content.Unload();
            foreach (Layer l in layers)
                l.Paused = true;
        }
        /// <summary>
        /// Unpause all the layers in the scene and reload the content.
        /// </summary>
        public void Unpause()
        {
            Content.Reload();
            foreach (Layer l in layers)
                l.Paused = false;
        }

        /// <summary>
        /// Exit the game cleanly.
        /// </summary>
        public void Exit()
        {
            Manager.Exit();
        }
        #endregion
    }
}
